package com.share.four.netty.http.helloworld;


import com.share.four.netty.marshalling.util.MarshallingCodeUtil;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

public class Server {

	@SuppressWarnings("resource")
	public static void main(String[] args) {
		//多线程事件循环器，用于接收client端的连接---->boss
		//一旦boss收到连接，会把连接信息注册到worker上
		NioEventLoopGroup bossGroup = new NioEventLoopGroup();
		//多线程事件循环器，用于处理实际的业务----->worker
		NioEventLoopGroup workerGroup = new NioEventLoopGroup();

		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup)
			.channel(NioServerSocketChannel.class)
			//option()是提供给NioServerSocketChannel用来接收进来的连接
			//保持连接
			.option(ChannelOption.SO_KEEPALIVE, true)
			
			/*
			 * 服务器端TCP内核模块维护有2个队列，我们称之为A、B吧
			 * 客户端向服务端connect的时候，会发送带有SYN标志的包（第一次握手）
			 * 服务器收到客户端发来的SYN时，向客户端发送SYN ACK确认（第二次握手）
			 * 此时TCP内核模块把客户端连接加入到A队列中，然后服务器收到客户端发送过来的ACK时（第三次握手），
			 * TCP内核模块把客户端连接从A队列移到B队列，连接完成，应用程序的accept会返回
			 * 也就是说accept从B队列中取出完成三次握手的连接。
			 * A队列和B队列的长度之和是backlog。当A，B队列的长度之和大于backlog时，新连接将会被TCP内核拒绝
			 * 所以，如果backlog过小，可能会出现accept速度跟不上,A、B队列满了，导致新的客户端无法连接
			 * 
			 * 要注意的是：backlog对程序支持的连接数并无影响，backlog影响的只是还没有被accept取出来的连接
			 */
			
			//设置tcp缓冲区---A队列中的连接数+B队列中的连接数=backlog  --------->相当于最大连接数是1024
			.option(ChannelOption.SO_BACKLOG, 1024)
			//设置发送缓冲区大小
			.option(ChannelOption.SO_SNDBUF, 32*1024)
			//设置接收缓冲区大小
			.option(ChannelOption.SO_RCVBUF, 32*1024)
			//设置日志
			.handler(new LoggingHandler(LogLevel.INFO))
			/*
			 * 这里的事件处理类经常会被用来处理一个最近的已经接收的Channel。
			 * ChannelInitializer是一个特殊的处理类，他的目的是帮助使用者配置一个新的Channel。
			 * 也许你想通过增加一些处理类比如DiscardServerHandle来配置一个新的Channel
			 *    或者其对应的ChannelPipeline来实现你的网络程序。
			 * 当你的程序变的复杂时，可能你会增加更多的处理类到pipline上，然后提取这些匿名类到最顶层的类上。
			 */
			.childHandler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					//添加http协议的配置
					ch.pipeline().addLast(new HttpServerCodec());
					ch.pipeline().addLast(new ServerHandler());
				}
			});
			

			//绑定指定的端口 进行监听--->异步的去监听
			ChannelFuture f = b.bind(12345).sync();
			System.out.println("server 启动。。。");
			System.out.println("open your web browser and navigate to  " + "http://127.0.0.1:12345/");

			//等待直到server socket被关闭----相当于Thread.sleep(Integer.MAX_VALUE);
			f.channel().closeFuture().sync();


		} catch (Exception e) {
			System.out.println(e);
		} finally {
			//关闭boss、worker多线程事件循环器
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}

}
